/*******************************************************************************
 * Copyright (c) 2007, Geophysical Center RAS
 * Sergey Berezin, Dmitry Voitsekhovskiy
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms,
 * with or without modification, are permitted provided
 * that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * - Neither the name of the Geophysical Center RAS nor the names
 * of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/
/* 
 * Helper geometry classes.
 *
 * Copyright (c) Sergey B. Berezin (s_berezin@cs.msu.su), 2002.
 *
 * You may freely distribute or modify this source code retaining the 
 * above copyright notice.
 *
 * This software is provided "as-is" and without guarantee or warrantee 
 * of any kind, expressed or implied :)
 */

using System;

namespace VisualEsse.DataEngine.Types
{

	public struct Vector2d {
		public double x,y;

		public Vector2d(double xx,double yy) {
			x = xx;
			y = yy;
		}

		public double Length {
			get {
				return Math.Sqrt(x*x + y*y);
			}
		}	

		public void Normalize() {
			double length = Length;
			if(length > 1e-10) {
				x /= length;
				y /= length;
			} else 
				x = y = 0;
		}

		public static Vector2d operator + (Vector2d a,Vector2d b) {
			return new Vector2d(a.x + b.x,a.y + b.y);
		}

		public static Vector2d operator - (Vector2d a,Vector2d b) {
			return new Vector2d(a.x - b.x,a.y - b.y);
		}

		public static Vector2d operator * (Vector2d a,double b) {
			return new Vector2d(a.x*b,a.y*b);
		}

		public static Vector2d operator * (double a,Vector2d b) {
			return new Vector2d(a*b.x,a*b.y);
		}

        public static Vector2d operator /(Vector2d a, double b)
        {
            return new Vector2d(a.x / b, a.y / b);
        }

        public override string ToString()
        {
            return string.Format("{0}, {1}", x, y);
        }
	}

	public struct Vector3d {
		public double x,y,z;
	
		public Vector3d(double xi,double yi,double zi) {
			x = xi;
			y = yi;
			z = zi;
		}

        public static Vector3d Empty
        {
            get { return new Vector3d(0, 0, 0); }
        }

		public double Length {
			get {
				return Math.Sqrt(x*x + y*y + z*z);
			}
		}

		public void MulAdd(Vector3d v1,Vector3d v2) {
			x += v1.y*v2.z-v2.y*v1.z;
			y += v1.z*v2.x-v2.z*v1.x;
			z += v1.x*v2.y-v2.x*v1.y;
		}

		public void MulAddN(Vector3d v1,Vector3d v2) {
			Vector3d v = v1*v2;
			v.Normalize();
			x += v.x;
			y += v.y;
			z += v.z;
		}

		public void Normalize() {
			double length = Math.Sqrt(x*x + y*y + z*z);
			if(length > 1e-8) {
				x /= length;
				y /= length;
				z /= length;
			}
		}

        /// <summary>
        /// Converts position in spherical coordinates (lat/lon/altitude) to cartesian (XYZ) coordinates.
        /// </summary>
        /// <param name="latitude">Latitude in decimal degrees</param>
        /// <param name="longitude">Longitude in decimal degrees</param>
        /// <param name="radius">Radius (OBS: not altitude)</param>
        /// <returns>Coordinates converted to cartesian (XYZ)</returns>
        public static Vector3d SphericalToCartesian(
            double latitude,
            double longitude,
            double radius
            )
        {
            latitude *= System.Math.PI / 180.0f;
            longitude *= System.Math.PI / 180.0f;

            double radCosLat = radius * Math.Cos(latitude);

            return new Vector3d(
                (radCosLat * Math.Cos(longitude)),
                (radCosLat * Math.Sin(longitude)),
                (radius * Math.Sin(latitude)));
        }

		static public Vector3d operator + (Vector3d v1,Vector3d v2) {
			return new Vector3d(v1.x + v2.x,v1.y + v2.y,v1.z + v2.z);
		}

		static public Vector3d operator - (Vector3d v1,Vector3d v2) {
			return new Vector3d(v1.x - v2.x,v1.y - v2.y,v1.z - v2.z);
		}

		static public Vector3d operator / (Vector3d v1,double d) {
			return new Vector3d(v1.x/d,v1.y/d,v1.z/d);
		}

		static public Vector3d operator * (Vector3d v1,Vector3d v2) {
			return new Vector3d(v1.y*v2.z-v2.y*v1.z,
				v1.z*v2.x-v2.z*v1.x,
				v1.x*v2.y-v2.x*v1.y);
		}

		static public Vector3d operator * (Vector3d v,double a) {
			return new Vector3d(v.x*a, v.y*a, v.z*a);
		}

		static public Vector3d operator * (double a,Vector3d v) {
			return new Vector3d(v.x*a, v.y*a, v.z*a);
		}

		static public double operator ^ (Vector3d v1,Vector3d v2) {
			return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
		}

        public override string ToString()
        {
            return String.Format("({0} {1} {2})", x, y, z);
        }

        public static Vector3d Min(Vector3d a, Vector3d b)
        {
            return new Vector3d(Math.Min(a.x, b.x), Math.Min(a.y, b.y), Math.Min(a.z, b.z));
        }

        public static Vector3d Max(Vector3d a, Vector3d b)
        {
            return new Vector3d(Math.Max(a.x, b.x), Math.Max(a.y, b.y), Math.Max(a.z, b.z));
        }

        public static Vector3d Cross(Vector3d left, Vector3d right)
        {
            Vector3d returnValue;
            returnValue.x = left.y * right.z - left.z * right.y;
            returnValue.y = left.z * right.x - left.x * right.z;
            returnValue.z = left.x * right.y - left.y * right.x;

            return returnValue;
        }
    }
}